#ifndef __MY_SERVER_H__
#define __MY_SERVER_H__
#include "Util.hpp"
#include "Room.hpp"
#include "Session.hpp"
#include "Matcher.hpp"
#include "OnlineManager.hpp"
#include "Database.hpp"

static const std::string default_root = "./WebRoot";
static const std::string not_found = "/404.html";

class Server
{
private:
    // 静态资源请求
    void StaticHandler(ws_server_t::connection_ptr &conn)
    {
        // 1.获取uri
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        // 2.拼接文件路径
        std::string filepath = _web_root + uri;
        // 访问目录，默认加上login.html
        if (filepath.back() == '/')
        {
            filepath += "index.html";
        }
        // 3.读取文件内容
        std::string body;
        if (!Util_File::Read(filepath, body))
        {
            // 读取文件失败，则读取404页面
            Util_File::Read(_web_root + not_found, body);
            conn->set_status(http_status_t::not_found);
        }
        else
        {
            conn->set_status(http_status_t::ok);
        }
        conn->append_header("Content-Length", std::to_string(body.size()));
        
        conn->set_body(body);
    }

    // 响应微服务请求
    void HttpResponser(ws_server_t::connection_ptr &conn, http_status_t code, const std::string &result)
    {
        Json::Value resq;
        if(!result.empty())
            resq["result"] = result;
        std::string body;
        Util_Json::Serialize(resq, body);
        conn->set_status(code);
        conn->append_header("Content-Type", "application/json");
        conn->set_body(body);
    }

    // 注册请求
    void RegisterHandler(ws_server_t::connection_ptr &conn)
    {
        // 1.获取请求正文
        websocketpp::http::parser::request req = conn->get_request();
        std::string req_body = req.get_body();
        // 2.反序列化
        Json::Value reg_info;
        if (!Util_Json::Deserialize(req_body, reg_info))
        {
            return HttpResponser(conn, http_status_t::bad_request, "错误的请求格式");
        }
        // 3.获取用户名和密码
        if (reg_info["name"].isNull() || reg_info["passwd"].isNull())
        {
            // 正常通过前端页面访问不会走到这一步
            return HttpResponser(conn, http_status_t::bad_request, "请输入用户名和密码");
        }
        std::string name = reg_info["name"].asString();
        std::string passwd = reg_info["passwd"].asString();
        // 4.插入新用户
        if (!_tb.Register(name, passwd))
        {
            return HttpResponser(conn, http_status_t::bad_request, "用户名已被占用");
        }
        // 5.注册成功
        return HttpResponser(conn, http_status_t::ok, "注册成功");
    }

    // 登录请求
    void LoginHandler(ws_server_t::connection_ptr &conn)
    {
        // 1.获取请求正文
        websocketpp::http::parser::request req = conn->get_request();
        std::string req_body = req.get_body();
        // 2.反序列化
        Json::Value login_info;
        if (!Util_Json::Deserialize(req_body, login_info))
        {
            return HttpResponser(conn, http_status_t::bad_request, "错误的请求格式");
        }
        // 3.获取用户名和密码
        if (login_info["name"].isNull() || login_info["passwd"].isNull())
        {
            // 正常通过前端页面访问不会走到这一步
            return HttpResponser(conn, http_status_t::bad_request, "请输入用户名和密码");
        }
        // 4.验证登录信息
        if (!_tb.LoginCheck(login_info))
        {
            return HttpResponser(conn, http_status_t::bad_request, "用户名或密码错误");
        }
        // 5.创建会话
        SessionPtr ssp = _sm.CreateSession(login_info["id"].asUInt());
        if (ssp.get() == nullptr)
        {
            LOG(LogLevel::ERROR) << "为用户[" << login_info["name"].asString() << "]创建会话失败";
            return HttpResponser(conn, http_status_t::internal_server_error, "创建会话失败");
        }
        _sm.SetSessionExpireTime(ssp->GetId(), SessionManager::TIMEOUT);
        // 6.设置cookie
        std::string cookie = "SSID=" + std::to_string(ssp->GetId());
        conn->append_header("Set-Cookie", cookie);
        return HttpResponser(conn, http_status_t::ok, "登录成功");
    }

    bool GetCookieVal(const std::string &cookie, const std::string &key, std::string &val)
    {
        // Cookie: SSID=XXX; XXX=XXX;
        std::vector<std::string> cookie_array;
        Util_String::Split(cookie, "; ", cookie_array);
        for (auto &kv : cookie_array)
        {
            std::vector<std::string> tmp;
            Util_String::Split(kv, "=", tmp);
            if (tmp.size() == 2 && tmp[0] == key)
            {
                val = tmp[1];
                return true;
            }
        }
        return false;
    }

    // 根据连接查找Session
    SessionPtr GetSessionByConn(ws_server_t::connection_ptr &conn)
    {
        // 1.获取cookie
        std::string cookie = conn->get_request_header("Cookie");
        if (cookie.empty())
        {
            // cookie获取失败，请求中没有cookie信息
            LOG(LogLevel::ERROR) << "cookie获取失败, 请求中没有cookie信息";
            return SessionPtr();
        }

        // 2.获取ssid
        std::string ssid_str;
        if (!GetCookieVal(cookie, "SSID", ssid_str))
        {
            // cookie中没有ssid字段
            LOG(LogLevel::ERROR) << "cookie中没有ssid字段";
            return SessionPtr();
        }

        // 3.查找会话信息
        return _sm.GetSession(std::stol(ssid_str));
    }

    // 用户信息请求
    void InfoHandler(ws_server_t::connection_ptr &conn)
    {
        SessionPtr ssp = GetSessionByConn(conn);
        if (ssp.get() == nullptr)
        {
            // 登录过期
            return HttpResponser(conn, http_status_t::bad_request, "找不到会话, 请重新登录");
        }

        // 4.取出用户信息并发送
        id_t uid = ssp->GetUser();
        Json::Value user_info;
        if (!_tb.SelectById(uid, user_info))
        {
            // 查询用户信息失败
            return HttpResponser(conn, http_status_t::internal_server_error, "查询不到用户信息");
        }
        std::string body;
        if (!Util_Json::Serialize(user_info, body))
        {
            // 序列化失败
            return HttpResponser(conn, http_status_t::internal_server_error, "响应消息序列化失败");
        }
        conn->set_status(http_status_t::ok);
        conn->append_header("Content-Type", "application/json");
        conn->set_body(body);
    }

    // http请求回调处理函数
    void HttpCallback(websocketpp::connection_hdl hdl)
    {
        ws_server_t::connection_ptr conn = _wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string method = req.get_method();
        std::string uri = req.get_uri();
        if (method == "POST" && uri == "/register")
            return RegisterHandler(conn);
        else if (method == "POST" && uri == "/login")
            return LoginHandler(conn);
        else if (method == "GET" && uri == "/info")
            return InfoHandler(conn);
        else
            return StaticHandler(conn);
    }

    // 回复websocket请求
    void WebSocketResponser(ws_server_t::connection_ptr &conn, std::string op_type, const std::string &result)
    {
        Json::Value resq = Response::WebSocket(op_type, result);
        return WebSocketResponser(conn, resq);
    }

    void WebSocketResponser(ws_server_t::connection_ptr &conn, const Json::Value& resq)
    {
        std::string body;
        Util_Json::Serialize(resq, body);
        conn->send(body);
    }

    // 建立游戏大厅长连接的处理
    void OpenHallHandler(ws_server_t::connection_ptr &conn)
    {
        // 建立游戏大厅长连接
        // 1.获取会话，验证登录
        SessionPtr ssp = GetSessionByConn(conn);
        if (ssp.get() == nullptr)
        {
            // 登录过期
            return WebSocketResponser(conn, "error", "会话不存在, 请重新登录!");
        }
        // 2.检查是否重复登录
        id_t uid = ssp->GetUser();
        if (_om.IsInGameHall(uid) || _om.IsInGameRoom(uid))
        {
            // 重复登录
            return WebSocketResponser(conn, "error", "已在其他地方登录!");
        }
        // 3.将当前客户端连接加入到游戏大厅
        _om.EnterGameHall(uid, conn);
        // 4.将Session设置为永久存在
        _sm.SetSessionExpireTime(ssp->GetId(), SessionManager::FOREVER);
        // 5.响应客户端游戏大厅连接建立成功
        return WebSocketResponser(conn, "hall_ready", "游戏大厅长连接建立成功!");
    }

    // 建立游戏房间长连接的处理
    void OpenRoomHandler(ws_server_t::connection_ptr &conn)
    {
        // 1.获取会话，验证登录
        SessionPtr ssp = GetSessionByConn(conn);
        if (ssp.get() == nullptr)
        {
            // 登录过期
            return WebSocketResponser(conn, "error", "会话不存在, 请重新登录!");
        }
        // 2.检查是否重复登录
        id_t uid = ssp->GetUser();
        if (_om.IsInGameHall(uid) || _om.IsInGameRoom(uid))
        {
            // 重复登录
            return WebSocketResponser(conn, "error", "已在其他地方登录!");
        }
        // 3.获取房间（匹配成功后，进入房间之前，Matcher会建立好房间）
        RoomPtr rp = _rm.GetRoomByUid(uid);
        if(rp.get() == nullptr)
        {
            return WebSocketResponser(conn, "error", "房间不存在");
        }
        // 4.将用户加入到OnlineManager的room中
        _om.EnterGameRoom(uid, conn);
        // 5.将Session设置为永久存在
        _sm.SetSessionExpireTime(ssp->GetId(), SessionManager::FOREVER);
        // 6.响应客户端游戏房间连接建立成功
        Json::Value resq = Response::RoomReady(ssp->GetUser(), rp->Id(), rp->WhitePlayer(), rp->BlackPlayer(), &_tb);
        return WebSocketResponser(conn, resq);
    }

    // websocket握手成功回调处理函数
    void OpenCallback(websocketpp::connection_hdl hdl)
    {
        ws_server_t::connection_ptr conn = _wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();

        if (uri == "/hall")
        {
            // 建立游戏大厅长连接
            return OpenHallHandler(conn);
        }
        else if (uri == "/room")
        {
            // 建立游戏房间长连接
            return OpenRoomHandler(conn);
        }
    }

    void CloseHallHandler(ws_server_t::connection_ptr& conn)
    {
        // 1.获取会话
        SessionPtr ssp = GetSessionByConn(conn);
        if(ssp.get() == nullptr)
        {
            LOG(LogLevel::ERROR) << "会话不存在!";
            return;
        }
        // 2.将玩家移出大厅
        _om.ExitGameHall(ssp->GetUser());
        // 3.设置会话生命周期
        _sm.SetSessionExpireTime(ssp->GetId(), SessionManager::TIMEOUT);
    }

    void CloseRoomHandler(ws_server_t::connection_ptr& conn)
    {
        // 1.获取会话
        SessionPtr ssp = GetSessionByConn(conn);
        if(ssp.get() == nullptr)
        {
            LOG(LogLevel::ERROR) << "会话不存在!";
            return;
        }
        // 2.将玩家移出房间
        _om.ExitGameRoom(ssp->GetUser());
        _rm.RemoveUser(ssp->GetUser());
        // 3.设置会话生命周期
        _sm.SetSessionExpireTime(ssp->GetId(), SessionManager::TIMEOUT);
    }

    // websocket连接关闭回调处理函数
    void CloseCallback(websocketpp::connection_hdl hdl)
    {
        ws_server_t::connection_ptr conn = _wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();

        if (uri == "/hall")
        {
            // 关闭游戏大厅长连接
            return CloseHallHandler(conn);
        }
        else if (uri == "/room")
        {
            // 关闭游戏房间长连接
            return CloseRoomHandler(conn);
        }
    }

    // 游戏大厅消息处理
    void MassageHallHandler(ws_server_t::connection_ptr& conn, ws_server_t::message_ptr msg)
    {
        // 1.获取会话
        SessionPtr ssp = GetSessionByConn(conn);
        if(ssp.get() == nullptr)
        {
            LOG(LogLevel::ERROR) << "会话不存在!";
            return WebSocketResponser(conn, "error", "会话不存在!");
        }
        // 2.获取请求信息
        std::string req_body = msg->get_payload();
        Json::Value req_json;
        if(!Util_Json::Deserialize(req_body, req_json))
        {
            return WebSocketResponser(conn, "error", "请求解析失败!");
        }
        // 3.处理消息
        if(req_json["op_type"].isNull())
        {
            return WebSocketResponser(conn, "error", "请求中缺少操作类型!");
        }
        std::string op_type = req_json["op_type"].asString();
        if(op_type == "match_start")
        {
            // 开始匹配：将玩家加入匹配队列
            if(_mc.Add(ssp->GetUser()))
                return WebSocketResponser(conn, op_type, "已开始匹配");
            return WebSocketResponser(conn, "error", "匹配失败");
        }
        else if(op_type == "match_stop")
        {
            // 取消匹配：将玩家从匹配队列移除
            if(_mc.Delete(ssp->GetUser()))
                return WebSocketResponser(conn, op_type, "已取消匹配");
            return WebSocketResponser(conn, "error", "取消匹配失败");
        }
        else
        {
            return WebSocketResponser(conn, "error", "未知的操作类型");
        }
    }

    // 游戏房间消息处理
    void MassageRoomHandler(ws_server_t::connection_ptr& conn, ws_server_t::message_ptr msg)
    {
        // 1.获取会话
        SessionPtr ssp = GetSessionByConn(conn);
        if(ssp.get() == nullptr)
        {
            LOG(LogLevel::ERROR) << "会话不存在";
            return WebSocketResponser(conn, "error", "会话不存在");
        }
        // 2.获取请求信息
        std::string req_body = msg->get_payload();
        Json::Value req_json;
        if(!Util_Json::Deserialize(req_body, req_json))
        {
            return WebSocketResponser(conn, "error", "请求解析失败");
        }
        // 3.获取房间
        RoomPtr rp = _rm.GetRoomByUid(ssp->GetUser());
        if(rp.get() == nullptr)
        {
            return WebSocketResponser(conn, "error", "房间不存在");
        }
        // 4.消息交给房间模块处理
        return rp->RequestHandler(req_json);
    }

    // websocket消息回调处理函数
    void MessageCallback(websocketpp::connection_hdl hdl, ws_server_t::message_ptr msg)
    {
        ws_server_t::connection_ptr conn = _wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();

        if (uri == "/hall")
        {
            // 游戏大厅消息处理
            return MassageHallHandler(conn, msg);
        }
        else if (uri == "/room")
        {
            // 游戏房间消息处理
            return MassageRoomHandler(conn, msg);
        }
    }

public:
    Server(const std::string &host, const std::string &user, const std::string &passwd, const std::string &db, const std::string &web_root = default_root)
        : _web_root(web_root), _tb(host, user, passwd, db), _rm(&_tb, &_om), _sm(&_wssrv), _mc(&_rm, &_om, &_tb)
    {
        // 2.设置日志等级
        _wssrv.set_access_channels(websocketpp::log::alevel::none);
        // 3.初始化asio调度器
        _wssrv.init_asio();
        _wssrv.set_reuse_addr(true);
        // 4.设置回调函数
        _wssrv.set_http_handler(std::bind(&Server::HttpCallback, this, std::placeholders::_1));
        _wssrv.set_open_handler(std::bind(&Server::OpenCallback, this, std::placeholders::_1));
        _wssrv.set_close_handler(std::bind(&Server::CloseCallback, this, std::placeholders::_1));
        _wssrv.set_message_handler(std::bind(&Server::MessageCallback, this, std::placeholders::_1, std::placeholders::_2));
    }
    void Start(uint32_t port)
    {
        _wssrv.listen(port);
        _wssrv.start_accept();
        _wssrv.run();
    }

private:
    std::string _web_root;
    OnlineManager _om;
    Table_User _tb;
    ws_server_t _wssrv;
    RoomManager _rm;
    SessionManager _sm;
    Matcher<3> _mc;
};

#endif